Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion public/css/app.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/js/app.js

Large diffs are not rendered by default.

55 changes: 55 additions & 0 deletions src/App/assets/js/components/_main.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,58 @@ function toggleTheme() {
localStorage.setItem('theme', next);
}
window.toggleTheme = toggleTheme;

function closeAllMdActionsMenus(except) {
document.querySelectorAll('.md-actions.open').forEach(function (wrapper) {
if (wrapper !== except) {
wrapper.classList.remove('open');
wrapper.querySelector('.md-actions-toggle').setAttribute('aria-expanded', 'false');
}
});
}

function toggleMdActionsMenu(button) {
var wrapper = button.closest('.md-actions');
var isOpen = wrapper.classList.contains('open');
closeAllMdActionsMenus(wrapper);
wrapper.classList.toggle('open', !isOpen);
button.setAttribute('aria-expanded', String(!isOpen));
}
window.toggleMdActionsMenu = toggleMdActionsMenu;

document.addEventListener('click', function (event) {
if (!event.target.closest('.md-actions')) {
closeAllMdActionsMenus();
}
});

document.addEventListener('keydown', function (event) {
if (event.key === 'Escape') {
closeAllMdActionsMenus();
}
});

document.addEventListener('click', function (event) {
var item = event.target.closest('[data-copy-url]');
if (!item) {
return;
}
event.preventDefault();

var url = item.getAttribute('data-copy-url');
var label = item.querySelector('.label');
var originalText = label.textContent;

fetch(url)
.then(function (response) { return response.text(); })
.then(function (markdown) { return navigator.clipboard.writeText(markdown); })
.then(function () {
label.textContent = 'Copied!';
setTimeout(function () { label.textContent = originalText; }, 1500);
})
.catch(function () {
navigator.clipboard.writeText(url);
});

closeAllMdActionsMenus();
});
127 changes: 126 additions & 1 deletion src/App/assets/scss/components/_blog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -502,4 +502,129 @@ $bp-tablet: 900px;
color: var(--text-dim);
flex-shrink: 0;
}
}
}

// ---------- Copy page / view as markdown / open in Claude dropdown ----------
.md-actions {
position: relative;
display: inline-flex;
}

.md-actions-toggle {
display: inline-flex;
align-items: stretch;
padding: 0;
border-radius: 8px;
background: var(--surface);
border: 1px solid var(--border);
color: var(--text-muted);
font-family: var(--sans);
font-size: 13px;
cursor: pointer;
overflow: hidden;
transition: box-shadow .15s ease, border-color .15s ease, color .15s ease;

svg {
width: 14px;
height: 14px;
flex-shrink: 0;
}

.chev {
width: 12px;
height: 12px;
transition: transform .15s ease;
}

&:hover {
border-color: var(--text-dim);
color: var(--text);
box-shadow: 0 4px 12px rgba(0, 0, 0, .12);
}
}

.md-actions-toggle-label {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
}

.md-actions-toggle-chev {
display: inline-flex;
align-items: center;
padding: 6px 10px;
border-left: 1px solid var(--border);
}

.md-actions.open .md-actions-toggle {
border-color: var(--text-dim);
color: var(--text);
box-shadow: 0 4px 12px rgba(0, 0, 0, .12);
}

.md-actions.open .md-actions-toggle .chev {
transform: rotate(180deg);
}

.md-actions-menu {
position: absolute;
top: calc(100% + 8px);
right: 0;
min-width: 240px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: 0 12px 32px rgba(0, 0, 0, .18);
padding: 6px;
display: none;
flex-direction: column;
gap: 2px;
z-index: 20;
}

.md-actions.open .md-actions-menu {
display: flex;
}

.md-actions-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 10px;
border-radius: 8px;
border: none;
background: transparent;
color: var(--text);
font-family: var(--sans);
font-size: 13px;
text-align: left;
text-decoration: none;
cursor: pointer;

svg {
width: 16px;
height: 16px;
color: var(--text-dim);
flex-shrink: 0;
}

&:hover {
background: var(--surface-2);
color: var(--amber);

svg {
color: var(--amber);
}
}
}

.md-actions-bar {
display: flex;
justify-content: flex-end;
margin-bottom: 12px;

& + h2 {
margin-top: 0;
}
}
34 changes: 34 additions & 0 deletions src/App/templates/layout/blog-post.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{% extends '@layout/default.html.twig' %}

{% block json_ld %} {{ include('@jsonld/' ~ article.category.slug ~ '/' ~ article.slug ~ '.jsonld.twig') }} {% endblock %}

{% block title %}{{ article.title }}{% endblock %}

{% block page_title %}
{{ include('@partial/title-section.html.twig', {
back_href: url('page::blog'),
back_label: 'Back to Blog',
badge: article.category.name,
article: article,
}) }}
{% endblock %}

{% block content %}
<div class="wrap post-body-wrap">
<div class="post-layout">

{{ include('@partial/left-menu.html.twig') }}

<article class="article">
<div class="entry">
{{ include('@partial/obsolete-warning.html.twig', {article: article}) }}
{{ include('@partial/markdown-actions.html.twig', {article: article}) }}
{% block body %}{% endblock %}
</div>

{{ include('@partial/post-nav.html.twig') }}
</article>

</div>
</div>
{% endblock %}
29 changes: 29 additions & 0 deletions src/App/templates/partial/markdown-actions.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{% set markdown_url = url('app::markdown-article', {categorySlug: article.category.slug, slug: article.slug}) %}
{% set claude_prompt = 'Read from ' ~ markdown_url ~ ' so I can ask questions about it.' %}
<div class="md-actions-bar">
<div class="md-actions">
<button type="button" class="md-actions-toggle" aria-haspopup="true" aria-expanded="false" onclick="toggleMdActionsMenu(this)">
<span class="md-actions-toggle-label">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h9l7 7v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"/><path d="M13 4v6h6"/></svg>
<span>Copy Page</span>
</span>
<span class="md-actions-toggle-chev">
<svg class="chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
</span>
</button>
<div class="md-actions-menu" role="menu">
<button type="button" class="md-actions-item" role="menuitem" data-copy-url="{{ markdown_url }}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg>
<span class="label">Copy Page (as Markdown)</span>
</button>
<a href="{{ markdown_url }}" target="_blank" rel="noopener noreferrer" class="md-actions-item" role="menuitem">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h9l7 7v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"/><path d="M13 4v6h6"/><path d="M8 15h8M8 11h4"/></svg>
<span class="label">View as Markdown</span>
</a>
<a href="https://claude.ai/new?q={{ claude_prompt|url_encode }}" target="_blank" rel="noopener noreferrer" class="md-actions-item" role="menuitem">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3l2.5 5.5L20 11l-5.5 2.5L12 19l-2.5-5.5L4 11l5.5-2.5z"/></svg>
<span class="label">Open in Claude</span>
</a>
</div>
</div>
</div>
2 changes: 1 addition & 1 deletion src/App/templates/partial/title-section.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
{% endif %}
</div>
{% endif %}
{% if author_github %}
{% if author_github and show_github is defined and show_github %}
<div class="post-meta-v2 mt-3">
<span class="meta-item">
Also on
Expand Down
1 change: 1 addition & 0 deletions src/Blog/templates/page/author-resource.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
back_label: 'Back to Blog',
badge: 'Author',
title: 'Articles by ' ~ author.name,
show_github: true,
author_github: author.github,
}) }}
{% endblock %}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,6 @@
{% extends '@layout/default.html.twig' %}
{% block json_ld %} {{ include('@jsonld/android/listen-for-android-install-referrer.jsonld.twig') }} {% endblock %}
{% extends '@layout/blog-post.html.twig' %}

{% block title %}Listen for Android install referrer{% endblock %}

{% block page_title %}
{{ include('@partial/title-section.html.twig', {
back_href: url('page::blog'),
back_label: 'Back to Blog',
badge: 'Android',
article: article,
}) }}
{% endblock %}

{% block content %}
<div class="wrap post-body-wrap">
<div class="post-layout">

{{ include('@partial/left-menu.html.twig') }}

<article class="article">
<div class="entry">
{{ include('@partial/obsolete-warning.html.twig', {article: article}) }}
{% block body %}
<h2>Getting Referrer Data at Install Time</h2>

<p class="mb-3">Have you ever wondered if Android market sends you information at the moment of app install? Wouldn't it be nice to create custom links to your Android application, including bits of information about the referrer, and send it directly to the app for processing at install? This could be a simple and accurate solution for mobile app install tracking, but I'm sure you can find this useful in many ways.</p>
Expand All @@ -43,11 +23,4 @@
</div>
</details>
</div>
</div>

{{ include('@partial/post-nav.html.twig') }}
</article>

</div>
</div>
{% endblock %}
Original file line number Diff line number Diff line change
@@ -1,26 +1,6 @@
{% extends '@layout/default.html.twig' %}
{% block json_ld %} {{ include('@jsonld/android/multiple-broadcast-receivers-in-the-same-app-for-the-same-action.jsonld.twig') }} {% endblock %}
{% extends '@layout/blog-post.html.twig' %}

{% block title %}Multiple broadcast receivers in the same app, for the same action{% endblock %}

{% block page_title %}
{{ include('@partial/title-section.html.twig', {
back_href: url('page::blog'),
back_label: 'Back to Blog',
badge: 'Android',
article: article,
}) }}
{% endblock %}

{% block content %}
<div class="wrap post-body-wrap">
<div class="post-layout">

{{ include('@partial/left-menu.html.twig') }}

<article class="article">
<div class="entry">
{{ include('@partial/obsolete-warning.html.twig', {article: article}) }}
{% block body %}
Did you come to a point where using multiple broadcast receivers to listen for the same intent, separatly, in the same android app, leads to unexpected results?
If that's the case, one broadcast receiver might consume the broadcasted intent, <a href="http://www.cillap.com/">online casino</a> leaving the others with nothing to receive.
This can be the case where you use 3rd party libraries with broadcast receivers defined.
Expand All @@ -43,11 +23,4 @@ The following is a solution for this kind of problem, a code snippet inspired by
</div>
</details>
</div>
</div>

{{ include('@partial/post-nav.html.twig') }}
</article>

</div>
</div>
{% endblock %}
Original file line number Diff line number Diff line change
@@ -1,26 +1,6 @@
{% extends '@layout/default.html.twig' %}
{% block json_ld %} {{ include('@jsonld/architecture/configprovider-bootstrap-modern-php-applications.jsonld.twig') }} {% endblock %}

{% block title %}ConfigProvider - Bootstrap Modern PHP Applications{% endblock %}

{% block page_title %}
{{ include('@partial/title-section.html.twig', {
back_href: url('page::blog'),
back_label: 'Back to Blog',
badge: 'Architecture',
article: article,
}) }}
{% endblock %}

{% block content %}
<div class="wrap post-body-wrap">
<div class="post-layout">

{{ include('@partial/left-menu.html.twig') }}
{% extends '@layout/blog-post.html.twig' %}

<article class="article">
<div class="entry">
{{ include('@partial/obsolete-warning.html.twig', {article: article}) }}
{% block body %}
<p class="mb-3">In PHP, the <code>ConfigProvider</code> is a class that is part of an application's bootstrap process. <strong>It's a class or callable that returns configuration data telling the platform which middleware should run, in what order, and sometimes under what conditions.</strong></p>

<p class="mb-3">If you're talking specifically about the ConfigProvider in the Laminas/Mezzio ecosystem, it's literally an array of configuration, settings, or anything else your application needs.</p>
Expand Down Expand Up @@ -184,11 +164,4 @@
</ul>

<p class="mb-3"></p>
</div>

{{ include('@partial/post-nav.html.twig') }}
</article>

</div>
</div>
{% endblock %}
Loading