instagram_connect 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -1
- data/README.md +29 -8
- data/app/assets/stylesheets/instagram_connect/application.css +106 -0
- data/app/helpers/instagram_connect/application_helper.rb +14 -0
- data/app/views/layouts/instagram_connect/application.html.erb +18 -9
- data/{lib/generators/instagram_connect/install/templates/create_instagram_connect_accounts.rb.tt → db/migrate/20260715072548_create_instagram_connect_accounts.rb} +1 -1
- data/{lib/generators/instagram_connect/install/templates/create_instagram_connect_conversations.rb.tt → db/migrate/20260715072549_create_instagram_connect_conversations.rb} +1 -1
- data/{lib/generators/instagram_connect/install/templates/create_instagram_connect_messages.rb.tt → db/migrate/20260715072550_create_instagram_connect_messages.rb} +1 -1
- data/{lib/generators/instagram_connect/install/templates/create_instagram_connect_inbound_messages.rb.tt → db/migrate/20260715072551_create_instagram_connect_inbound_messages.rb} +1 -1
- data/{lib/generators/instagram_connect/install/templates/create_instagram_connect_comments.rb.tt → db/migrate/20260715072552_create_instagram_connect_comments.rb} +1 -1
- data/lib/generators/instagram_connect/install/install_generator.rb +7 -21
- data/lib/generators/instagram_connect/install/templates/instagram_connect.rb.tt +16 -6
- data/lib/instagram_connect/configuration.rb +32 -1
- data/lib/instagram_connect/engine.rb +20 -0
- data/lib/instagram_connect/version.rb +1 -1
- data/lib/instagram_connect.rb +8 -0
- metadata +8 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: afc5d08b4207b0268104bbf7a4955406b26298e4da4406bd5034a93b8a43982f
|
|
4
|
+
data.tar.gz: 4d436bf9a398fc745f4a594b1346cd8315a949391213f83c45cc52c07694e993
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3d4894411aa0ad5924a8d4d163215a880e8344aa3306a814400cd73e2ad717d5bca7f4a4284c4baa81162af98cabc987983c4715c3eaab8b0fb16ba9a69321ec
|
|
7
|
+
data.tar.gz: 9ce313d2dd27ce96e913da61d46eb9f4e87bcbac7681fcd6963447759286b6419a7453d50821d9909fe570eca0fe98b5620fc709ead569e4f8c88f3752b635c9
|
data/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,28 @@ All notable changes to this project are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/) and the project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
-
## [0.
|
|
7
|
+
## [0.2.0]
|
|
8
|
+
|
|
9
|
+
True plug-and-play: the gem now owns the data model and the UI, so a host adds
|
|
10
|
+
**only configuration** — no copied migrations, views, or CSS.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- **Migrations ship in the gem** (`db/migrate`) and run in place via an appended
|
|
14
|
+
migration path — the host just runs `bin/rails db:migrate`. The install
|
|
15
|
+
generator no longer copies migrations. The gem owns the schema, versioned with it.
|
|
16
|
+
- **Self-contained, themeable UI.** The gem ships its own layout + stylesheet and
|
|
17
|
+
renders in its own chrome by default (`inherit_host_layout` now defaults to
|
|
18
|
+
`false`, like an admin engine). Tint it entirely from the initializer via
|
|
19
|
+
`config.theme = { primary:, font:, radius:, … }` (merged over sensible defaults) —
|
|
20
|
+
no CSS or views in the host app.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- Enable `encrypts :access_token` at runtime via an engine `to_prepare` hook, so
|
|
24
|
+
access tokens are encrypted at rest in host apps. Previously the decoration only
|
|
25
|
+
ran in the gem's own test suite, so a host stored tokens in plain text. Opt out
|
|
26
|
+
with `config.encrypt_tokens = false`.
|
|
27
|
+
|
|
28
|
+
## [0.1.0]
|
|
8
29
|
|
|
9
30
|
Initial release. A mountable Rails engine for Instagram over the official Meta Graph API.
|
|
10
31
|
|
data/README.md
CHANGED
|
@@ -46,12 +46,15 @@ gem "instagram_connect"
|
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
bundle install
|
|
49
|
-
bin/rails g instagram_connect:install # initializer +
|
|
50
|
-
bin/rails db:migrate
|
|
49
|
+
bin/rails g instagram_connect:install # writes the initializer + mounts the engine
|
|
50
|
+
bin/rails db:migrate # runs the gem's migrations in place
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
The install generator writes `config/initializers/instagram_connect.rb
|
|
54
|
-
engine at `/instagram
|
|
53
|
+
The install generator writes `config/initializers/instagram_connect.rb` and mounts
|
|
54
|
+
the engine at `/instagram`. **Migrations ship inside the gem and run in place** —
|
|
55
|
+
nothing is copied into your app, so every adopter gets exactly the same schema,
|
|
56
|
+
versioned with the gem. That's the whole install: **configuration only, no copied
|
|
57
|
+
migrations, views, or CSS.**
|
|
55
58
|
|
|
56
59
|
## Configuration
|
|
57
60
|
|
|
@@ -98,12 +101,30 @@ same `verify_token` you configured. Subscribe the fields you need: `messages`,
|
|
|
98
101
|
|
|
99
102
|
## The inbox UI
|
|
100
103
|
|
|
101
|
-
Mounted at `/instagram`: a DM inbox
|
|
102
|
-
moderation
|
|
104
|
+
Mounted at `/instagram`: a self-contained, self-styled DM inbox (window-aware reply
|
|
105
|
+
composer), comment moderation, and publishing — its own chrome, like an admin engine.
|
|
106
|
+
Nothing to build in your app.
|
|
107
|
+
|
|
108
|
+
### Theming
|
|
109
|
+
|
|
110
|
+
Tint the whole UI to your brand from the initializer — no CSS or view files in your
|
|
111
|
+
app. Any subset of keys is merged over the gem defaults:
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
config.theme = {
|
|
115
|
+
primary: "#0057a8",
|
|
116
|
+
font: "Inter, system-ui, sans-serif",
|
|
117
|
+
radius: "12px"
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Deeper control (optional): render inside your own layout with
|
|
122
|
+
`config.inherit_host_layout = true` (then add `<%= instagram_connect_styles %>` to
|
|
123
|
+
your `<head>`), or copy the views/controllers to fully override them:
|
|
103
124
|
|
|
104
125
|
```bash
|
|
105
|
-
bin/rails g instagram_connect:views #
|
|
106
|
-
bin/rails g instagram_connect:controllers #
|
|
126
|
+
bin/rails g instagram_connect:views # copy views into app/views/instagram_connect
|
|
127
|
+
bin/rails g instagram_connect:controllers # copy controllers to override
|
|
107
128
|
```
|
|
108
129
|
|
|
109
130
|
## CLI
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/* instagram_connect — self-contained UI styles. All colours/spacing come from
|
|
2
|
+
CSS variables set from config.theme, so a host tints the whole UI from its
|
|
3
|
+
initializer without touching any CSS or view. */
|
|
4
|
+
|
|
5
|
+
.ic-body {
|
|
6
|
+
margin: 0;
|
|
7
|
+
background: var(--ic-bg);
|
|
8
|
+
color: var(--ic-text);
|
|
9
|
+
font-family: var(--ic-font);
|
|
10
|
+
font-size: 15px;
|
|
11
|
+
line-height: 1.5;
|
|
12
|
+
-webkit-font-smoothing: antialiased;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.ic-app { max-width: 900px; margin: 0 auto; padding: 0 0 4rem; }
|
|
16
|
+
|
|
17
|
+
.ic-appbar {
|
|
18
|
+
display: flex; align-items: center; gap: 1.25rem;
|
|
19
|
+
padding: 0.9rem 1rem; border-bottom: 1px solid var(--ic-border);
|
|
20
|
+
background: var(--ic-surface); position: sticky; top: 0; z-index: 5;
|
|
21
|
+
}
|
|
22
|
+
.ic-appbar__brand { font-weight: 700; letter-spacing: -0.01em; }
|
|
23
|
+
.ic-appbar__nav { display: flex; gap: 1rem; }
|
|
24
|
+
.ic-appbar__nav a { color: var(--ic-muted); text-decoration: none; font-weight: 500; }
|
|
25
|
+
.ic-appbar__nav a:hover { color: var(--ic-primary); }
|
|
26
|
+
|
|
27
|
+
.ic-main { padding: 1rem; }
|
|
28
|
+
.ic-screen { display: flex; flex-direction: column; gap: 1rem; }
|
|
29
|
+
.ic-pagehead h1 { margin: 0.25rem 0 0; font-size: 1.5rem; letter-spacing: -0.02em; }
|
|
30
|
+
.ic-pagehead p { margin: 0.15rem 0 0; color: var(--ic-muted); }
|
|
31
|
+
|
|
32
|
+
.ic-inbox__title, .ic-comments__title, .ic-posts__title, .ic-publish__title,
|
|
33
|
+
.ic-thread-view__title { margin: 0 0 1rem; font-size: 1.5rem; letter-spacing: -0.02em; }
|
|
34
|
+
|
|
35
|
+
.ic-card, .ic-inbox, .ic-comments, .ic-posts, .ic-publish {
|
|
36
|
+
background: var(--ic-surface); border: 1px solid var(--ic-border);
|
|
37
|
+
border-radius: var(--ic-radius); padding: 1rem;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Inbox rows */
|
|
41
|
+
.ic-conversations, .ic-comments__list, .ic-posts__list { list-style: none; margin: 0; padding: 0; }
|
|
42
|
+
.ic-conversation, .ic-comment, .ic-post {
|
|
43
|
+
display: flex; flex-direction: column; gap: 0.25rem;
|
|
44
|
+
padding: 0.75rem 0.5rem; border-bottom: 1px solid var(--ic-border);
|
|
45
|
+
}
|
|
46
|
+
.ic-conversation:last-child, .ic-comment:last-child, .ic-post:last-child { border-bottom: 0; }
|
|
47
|
+
.ic-conversation__link { display: flex; align-items: baseline; gap: 0.5rem; text-decoration: none; color: inherit; }
|
|
48
|
+
.ic-conversation__name { font-weight: 600; }
|
|
49
|
+
.ic-conversation__preview, .ic-comment__text { color: var(--ic-muted); font-size: 0.9rem; }
|
|
50
|
+
.ic-conversation__unread {
|
|
51
|
+
margin-left: auto; background: var(--ic-primary); color: var(--ic-primary-contrast);
|
|
52
|
+
border-radius: 9999px; padding: 0.05rem 0.5rem; font-size: 0.75rem; font-weight: 700;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Thread + bubbles */
|
|
56
|
+
.ic-thread-view__back { margin: 0 0 0.5rem; }
|
|
57
|
+
.ic-thread-view__back a, .ic-posts__new, .ic-post__link { color: var(--ic-primary); text-decoration: none; }
|
|
58
|
+
.ic-thread { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1rem; }
|
|
59
|
+
.ic-message { max-width: 78%; padding: 0.6rem 0.8rem; border-radius: var(--ic-radius); }
|
|
60
|
+
.ic-message--inbound { align-self: flex-start; background: var(--ic-customer-bubble); }
|
|
61
|
+
.ic-message--outbound { align-self: flex-end; background: var(--ic-staff-bubble); }
|
|
62
|
+
.ic-message__body { white-space: pre-wrap; }
|
|
63
|
+
.ic-message__meta { display: block; margin-top: 0.25rem; font-size: 0.72rem; color: var(--ic-muted); }
|
|
64
|
+
|
|
65
|
+
/* Composer + forms */
|
|
66
|
+
.ic-composer, .ic-publish__form { display: flex; flex-direction: column; gap: 0.5rem; }
|
|
67
|
+
.ic-composer__input, .ic-field__input, .ic-input, textarea, input[type="url"], input[type="text"] {
|
|
68
|
+
width: 100%; box-sizing: border-box; padding: 0.6rem 0.75rem;
|
|
69
|
+
border: 1px solid var(--ic-border); border-radius: var(--ic-radius);
|
|
70
|
+
font: inherit; color: inherit; background: var(--ic-surface);
|
|
71
|
+
}
|
|
72
|
+
.ic-field { display: flex; flex-direction: column; gap: 0.3rem; margin-bottom: 0.75rem; }
|
|
73
|
+
.ic-field label, .ic-label { font-weight: 600; font-size: 0.85rem; }
|
|
74
|
+
.ic-composer__note, .ic-publish__hint { color: var(--ic-warn); font-size: 0.85rem; }
|
|
75
|
+
.ic-composer__closed { color: var(--ic-muted); }
|
|
76
|
+
|
|
77
|
+
/* Buttons */
|
|
78
|
+
.ic-btn, .ic-composer__send, .ic-publish__submit,
|
|
79
|
+
button[type="submit"] {
|
|
80
|
+
display: inline-block; padding: 0.55rem 1rem; border: 0; cursor: pointer;
|
|
81
|
+
border-radius: var(--ic-radius); font: inherit; font-weight: 600;
|
|
82
|
+
background: var(--ic-primary); color: var(--ic-primary-contrast); text-decoration: none;
|
|
83
|
+
}
|
|
84
|
+
.ic-btn--secondary { background: var(--ic-surface); color: var(--ic-text); border: 1px solid var(--ic-border); }
|
|
85
|
+
.ic-comment__actions { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
|
|
86
|
+
.ic-comment__actions form { display: inline-flex; gap: 0.3rem; }
|
|
87
|
+
.ic-comment__actions button { padding: 0.35rem 0.7rem; font-size: 0.85rem; }
|
|
88
|
+
.ic-comment--hidden { opacity: 0.55; }
|
|
89
|
+
|
|
90
|
+
/* Badges / flashes / empty */
|
|
91
|
+
.ic-badge { display: inline-block; padding: 0.05rem 0.5rem; border-radius: 9999px; font-size: 0.72rem; font-weight: 700; }
|
|
92
|
+
.ic-badge--green { background: color-mix(in srgb, var(--ic-ok) 15%, transparent); color: var(--ic-ok); }
|
|
93
|
+
.ic-badge--red { background: color-mix(in srgb, var(--ic-err) 15%, transparent); color: var(--ic-err); }
|
|
94
|
+
.ic-badge--amber { background: color-mix(in srgb, var(--ic-warn) 15%, transparent); color: var(--ic-warn); }
|
|
95
|
+
.ic-empty { color: var(--ic-muted); padding: 1rem 0.5rem; }
|
|
96
|
+
.ic-flash { margin: 0 0 0.75rem; padding: 0.6rem 0.9rem; border-radius: var(--ic-radius); }
|
|
97
|
+
.ic-flash--notice { background: color-mix(in srgb, var(--ic-ok) 12%, transparent); color: var(--ic-ok); }
|
|
98
|
+
.ic-flash--alert { background: color-mix(in srgb, var(--ic-err) 12%, transparent); color: var(--ic-err); }
|
|
99
|
+
|
|
100
|
+
.ic-posts__actions { margin: 0 0 0.75rem; }
|
|
101
|
+
.ic-posts__quota { color: var(--ic-muted); font-size: 0.9rem; }
|
|
102
|
+
|
|
103
|
+
@media (max-width: 480px) {
|
|
104
|
+
.ic-message { max-width: 88%; }
|
|
105
|
+
.ic-appbar { gap: 0.75rem; }
|
|
106
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module InstagramConnect
|
|
2
|
+
module ApplicationHelper
|
|
3
|
+
# Emits a <style> block: the theme values from config as CSS variables,
|
|
4
|
+
# followed by the gem's base stylesheet. Included automatically in the gem's
|
|
5
|
+
# own layout; a host inheriting its own layout can drop this into its <head>.
|
|
6
|
+
def instagram_connect_styles
|
|
7
|
+
vars = InstagramConnect.configuration.resolved_theme.map do |key, value|
|
|
8
|
+
"--ic-#{key.to_s.tr('_', '-')}: #{value};"
|
|
9
|
+
end.join(" ")
|
|
10
|
+
|
|
11
|
+
content_tag(:style, ":root { #{vars} }\n#{InstagramConnect.base_css}".html_safe)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
|
-
<title>Instagram
|
|
4
|
+
<title>Instagram</title>
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<%= csrf_meta_tags %>
|
|
7
|
+
<%= csp_meta_tag if respond_to?(:csp_meta_tag) %>
|
|
8
|
+
<%= instagram_connect_styles %>
|
|
7
9
|
<%= yield :head %>
|
|
8
10
|
</head>
|
|
9
11
|
<body class="ic-body">
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
<div class="ic-app">
|
|
13
|
+
<header class="ic-appbar">
|
|
14
|
+
<span class="ic-appbar__brand">Instagram</span>
|
|
15
|
+
<nav class="ic-appbar__nav">
|
|
16
|
+
<%= link_to "Inbox", conversations_path %>
|
|
17
|
+
<%= link_to "Comments", comments_path %>
|
|
18
|
+
<%= link_to "Publish", posts_path %>
|
|
19
|
+
</nav>
|
|
20
|
+
</header>
|
|
21
|
+
|
|
22
|
+
<% if flash[:notice] %><p class="ic-flash ic-flash--notice"><%= flash[:notice] %></p><% end %>
|
|
23
|
+
<% if flash[:alert] %><p class="ic-flash ic-flash--alert"><%= flash[:alert] %></p><% end %>
|
|
24
|
+
|
|
25
|
+
<main class="ic-main"><%= yield %></main>
|
|
26
|
+
</div>
|
|
18
27
|
</body>
|
|
19
28
|
</html>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class CreateInstagramConnectAccounts < ActiveRecord::Migration[
|
|
1
|
+
class CreateInstagramConnectAccounts < ActiveRecord::Migration[7.1]
|
|
2
2
|
def change
|
|
3
3
|
create_table :instagram_connect_accounts do |t|
|
|
4
4
|
t.string :auth_path, null: false
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class CreateInstagramConnectConversations < ActiveRecord::Migration[
|
|
1
|
+
class CreateInstagramConnectConversations < ActiveRecord::Migration[7.1]
|
|
2
2
|
def change
|
|
3
3
|
create_table :instagram_connect_conversations do |t|
|
|
4
4
|
t.bigint :account_id, null: false
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class CreateInstagramConnectMessages < ActiveRecord::Migration[
|
|
1
|
+
class CreateInstagramConnectMessages < ActiveRecord::Migration[7.1]
|
|
2
2
|
def change
|
|
3
3
|
create_table :instagram_connect_messages do |t|
|
|
4
4
|
t.bigint :conversation_id, null: false
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class CreateInstagramConnectInboundMessages < ActiveRecord::Migration[
|
|
1
|
+
class CreateInstagramConnectInboundMessages < ActiveRecord::Migration[7.1]
|
|
2
2
|
def change
|
|
3
3
|
create_table :instagram_connect_inbound_messages do |t|
|
|
4
4
|
t.string :ig_message_id, null: false
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class CreateInstagramConnectComments < ActiveRecord::Migration[
|
|
1
|
+
class CreateInstagramConnectComments < ActiveRecord::Migration[7.1]
|
|
2
2
|
def change
|
|
3
3
|
create_table :instagram_connect_comments do |t|
|
|
4
4
|
t.bigint :account_id, null: false
|
|
@@ -1,27 +1,13 @@
|
|
|
1
1
|
require "rails/generators"
|
|
2
|
-
require "rails/generators/active_record"
|
|
3
2
|
|
|
4
3
|
module InstagramConnect
|
|
5
4
|
module Generators
|
|
6
|
-
# `rails g instagram_connect:install` — writes the initializer
|
|
7
|
-
# engine
|
|
5
|
+
# `rails g instagram_connect:install` — writes the initializer and mounts the
|
|
6
|
+
# engine. Migrations ship inside the gem and run in place (just
|
|
7
|
+
# `rails db:migrate`), so there is nothing to copy.
|
|
8
8
|
class InstallGenerator < ::Rails::Generators::Base
|
|
9
|
-
include ::Rails::Generators::Migration
|
|
10
|
-
|
|
11
9
|
source_root File.expand_path("templates", __dir__)
|
|
12
10
|
|
|
13
|
-
MIGRATIONS = %w[
|
|
14
|
-
create_instagram_connect_accounts
|
|
15
|
-
create_instagram_connect_conversations
|
|
16
|
-
create_instagram_connect_messages
|
|
17
|
-
create_instagram_connect_inbound_messages
|
|
18
|
-
create_instagram_connect_comments
|
|
19
|
-
].freeze
|
|
20
|
-
|
|
21
|
-
def self.next_migration_number(dirname)
|
|
22
|
-
ActiveRecord::Generators::Base.next_migration_number(dirname)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
11
|
def create_initializer
|
|
26
12
|
template "instagram_connect.rb.tt", "config/initializers/instagram_connect.rb"
|
|
27
13
|
end
|
|
@@ -30,10 +16,10 @@ module InstagramConnect
|
|
|
30
16
|
route %(mount InstagramConnect::Engine => "/instagram")
|
|
31
17
|
end
|
|
32
18
|
|
|
33
|
-
def
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
19
|
+
def show_readme
|
|
20
|
+
say "\ninstagram_connect installed. Next:"
|
|
21
|
+
say " bin/rails db:migrate # runs the engine's migrations in place"
|
|
22
|
+
say " Configure config/initializers/instagram_connect.rb, then mount is at /instagram\n"
|
|
37
23
|
end
|
|
38
24
|
end
|
|
39
25
|
end
|
|
@@ -11,25 +11,35 @@ InstagramConnect.configure do |config|
|
|
|
11
11
|
# Pin the Graph API version.
|
|
12
12
|
config.graph_version = "v21.0"
|
|
13
13
|
|
|
14
|
-
# Rails integration.
|
|
14
|
+
# Rails integration. The engine's controllers inherit this controller, so they
|
|
15
|
+
# pick up its authentication before_actions automatically.
|
|
15
16
|
config.parent_controller = "ApplicationController"
|
|
16
17
|
|
|
17
|
-
# Require operators to be signed in
|
|
18
|
-
#
|
|
18
|
+
# Require operators to be signed in (runs in controller context — call your
|
|
19
|
+
# host auth here):
|
|
19
20
|
# config.authenticate_with = -> { authenticate_user! }
|
|
20
21
|
|
|
21
22
|
# Attribute outbound replies to the current operator:
|
|
22
23
|
# config.current_user_id_resolver = -> { current_user&.id }
|
|
23
24
|
|
|
24
25
|
# Where the OAuth callback returns after connecting an account.
|
|
25
|
-
config.after_connect_redirect = "/instagram"
|
|
26
|
+
config.after_connect_redirect = "/instagram/conversations"
|
|
27
|
+
|
|
28
|
+
# The gem ships a complete, self-styled UI. Tint it to your brand here — no
|
|
29
|
+
# CSS or view files needed in your app. Any subset of keys is merged over the
|
|
30
|
+
# gem defaults:
|
|
31
|
+
# config.theme = {
|
|
32
|
+
# primary: "#2563eb",
|
|
33
|
+
# font: "Inter, system-ui, sans-serif",
|
|
34
|
+
# radius: "12px"
|
|
35
|
+
# }
|
|
26
36
|
|
|
27
37
|
# Optional host hooks — react to inbound events (notifications, AI, …):
|
|
28
38
|
# config.on_message = ->(message) { }
|
|
29
39
|
# config.on_comment = ->(comment) { }
|
|
30
40
|
|
|
31
41
|
# Access tokens are encrypted at rest via Active Record Encryption. Run
|
|
32
|
-
# `bin/rails db:encryption:init` once, or set this to false if your app has
|
|
33
|
-
#
|
|
42
|
+
# `bin/rails db:encryption:init` once, or set this to false if your app has no
|
|
43
|
+
# encryption configured.
|
|
34
44
|
# config.encrypt_tokens = false
|
|
35
45
|
end
|
|
@@ -12,6 +12,26 @@ module InstagramConnect
|
|
|
12
12
|
class Configuration
|
|
13
13
|
AUTH_PATHS = %i[instagram_login facebook_login].freeze
|
|
14
14
|
|
|
15
|
+
# The gem ships a complete, self-styled UI. A host tints it to its brand by
|
|
16
|
+
# overriding any of these via `config.theme = { primary: "#0057a8", ... }` —
|
|
17
|
+
# no CSS or view files needed in the host app.
|
|
18
|
+
DEFAULT_THEME = {
|
|
19
|
+
primary: "#2563eb",
|
|
20
|
+
primary_contrast: "#ffffff",
|
|
21
|
+
bg: "#f7f8fa",
|
|
22
|
+
surface: "#ffffff",
|
|
23
|
+
text: "#111827",
|
|
24
|
+
muted: "#6b7280",
|
|
25
|
+
border: "#e5e7eb",
|
|
26
|
+
radius: "12px",
|
|
27
|
+
font: "Inter, system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
|
|
28
|
+
customer_bubble: "#f1f3f5",
|
|
29
|
+
staff_bubble: "#eef2ff",
|
|
30
|
+
ok: "#16a34a",
|
|
31
|
+
warn: "#d97706",
|
|
32
|
+
err: "#dc2626"
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
15
35
|
attr_accessor :app_id, :app_secret, :verify_token,
|
|
16
36
|
:graph_version, :redirect_uri, :encrypt_tokens,
|
|
17
37
|
:parent_controller, :authenticate_with, :current_user_id_resolver,
|
|
@@ -19,6 +39,7 @@ module InstagramConnect
|
|
|
19
39
|
:logger, :default_per_page, :inherit_host_layout,
|
|
20
40
|
:media_max_bytes, :allowed_media_types, :after_connect_redirect
|
|
21
41
|
attr_reader :auth_path
|
|
42
|
+
attr_writer :theme
|
|
22
43
|
|
|
23
44
|
def initialize
|
|
24
45
|
@auth_path = (ENV["INSTAGRAM_CONNECT_AUTH_PATH"] || "instagram_login").to_sym
|
|
@@ -36,7 +57,12 @@ module InstagramConnect
|
|
|
36
57
|
@on_postback = nil
|
|
37
58
|
@logger = Logger.new($stdout)
|
|
38
59
|
@default_per_page = 25
|
|
39
|
-
|
|
60
|
+
# Default false: the gem renders in its own self-contained layout (its own
|
|
61
|
+
# chrome + styles), like an admin engine. Set true to render inside the
|
|
62
|
+
# host's application layout instead (then include the gem styles in your
|
|
63
|
+
# <head> via `instagram_connect_styles`).
|
|
64
|
+
@inherit_host_layout = false
|
|
65
|
+
@theme = {}
|
|
40
66
|
# Where the OAuth callback redirects after connecting an account.
|
|
41
67
|
@after_connect_redirect = "/"
|
|
42
68
|
@media_max_bytes = 25 * 1024 * 1024
|
|
@@ -52,6 +78,11 @@ module InstagramConnect
|
|
|
52
78
|
@auth_path = value&.to_sym
|
|
53
79
|
end
|
|
54
80
|
|
|
81
|
+
# The host's theme overrides merged over the gem defaults.
|
|
82
|
+
def resolved_theme
|
|
83
|
+
DEFAULT_THEME.merge(@theme || {})
|
|
84
|
+
end
|
|
85
|
+
|
|
55
86
|
# Called by .configure at boot. Validates only structural config (a known
|
|
56
87
|
# auth_path) so an unconfigured host can still boot; secret presence is
|
|
57
88
|
# enforced lazily at the point of use (Client / OAuth).
|
|
@@ -14,6 +14,17 @@ module InstagramConnect
|
|
|
14
14
|
app.config.filter_parameters += %i[access_token app_secret verify_token hub_verify_token]
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
# Ship migrations inside the gem and run them in place — the host just runs
|
|
18
|
+
# `rails db:migrate`, no copying. The gem owns the data model, so every
|
|
19
|
+
# adopter gets exactly the same schema, versioned with the gem.
|
|
20
|
+
initializer "instagram_connect.append_migrations" do |app|
|
|
21
|
+
unless app.root.to_s == root.to_s
|
|
22
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
|
23
|
+
app.config.paths["db/migrate"] << expanded_path
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
17
28
|
# Apply any host-provided config.instagram_connect = { ... } hash onto the
|
|
18
29
|
# gem Configuration via setters (in addition to the initializer DSL).
|
|
19
30
|
initializer "instagram_connect.configure" do |app|
|
|
@@ -24,5 +35,14 @@ module InstagramConnect
|
|
|
24
35
|
end
|
|
25
36
|
end
|
|
26
37
|
end
|
|
38
|
+
|
|
39
|
+
# Enable at-rest token encryption on boot (and re-apply on each dev reload,
|
|
40
|
+
# since the Account class is reloaded fresh). Without this the `encrypts`
|
|
41
|
+
# decoration never runs in a host app and access tokens persist in plain
|
|
42
|
+
# text. Opt out with `config.encrypt_tokens = false` (e.g. no Active Record
|
|
43
|
+
# Encryption configured).
|
|
44
|
+
config.to_prepare do
|
|
45
|
+
InstagramConnect::Account.enable_token_encryption! if InstagramConnect.configuration.encrypt_tokens
|
|
46
|
+
end
|
|
27
47
|
end
|
|
28
48
|
end
|
data/lib/instagram_connect.rb
CHANGED
|
@@ -41,6 +41,14 @@ module InstagramConnect
|
|
|
41
41
|
def reset!
|
|
42
42
|
@configuration = Configuration.new
|
|
43
43
|
end
|
|
44
|
+
|
|
45
|
+
# The gem's base stylesheet, inlined into its layout so the UI is styled
|
|
46
|
+
# with no host asset-pipeline setup required.
|
|
47
|
+
def base_css
|
|
48
|
+
@base_css ||= File.read(
|
|
49
|
+
File.expand_path("../app/assets/stylesheets/instagram_connect/application.css", __dir__)
|
|
50
|
+
)
|
|
51
|
+
end
|
|
44
52
|
end
|
|
45
53
|
end
|
|
46
54
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: instagram_connect
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kshitiz Sinha
|
|
@@ -70,6 +70,7 @@ files:
|
|
|
70
70
|
- MIT-LICENSE
|
|
71
71
|
- README.md
|
|
72
72
|
- Rakefile
|
|
73
|
+
- app/assets/stylesheets/instagram_connect/application.css
|
|
73
74
|
- app/controllers/instagram_connect/application_controller.rb
|
|
74
75
|
- app/controllers/instagram_connect/comments_controller.rb
|
|
75
76
|
- app/controllers/instagram_connect/conversations_controller.rb
|
|
@@ -77,6 +78,7 @@ files:
|
|
|
77
78
|
- app/controllers/instagram_connect/oauth_controller.rb
|
|
78
79
|
- app/controllers/instagram_connect/posts_controller.rb
|
|
79
80
|
- app/controllers/instagram_connect/webhooks_controller.rb
|
|
81
|
+
- app/helpers/instagram_connect/application_helper.rb
|
|
80
82
|
- app/jobs/instagram_connect/application_job.rb
|
|
81
83
|
- app/jobs/instagram_connect/ingest_job.rb
|
|
82
84
|
- app/jobs/instagram_connect/refresh_tokens_job.rb
|
|
@@ -99,16 +101,16 @@ files:
|
|
|
99
101
|
- app/views/layouts/instagram_connect/application.html.erb
|
|
100
102
|
- bin/instagram_connect
|
|
101
103
|
- config/routes.rb
|
|
104
|
+
- db/migrate/20260715072548_create_instagram_connect_accounts.rb
|
|
105
|
+
- db/migrate/20260715072549_create_instagram_connect_conversations.rb
|
|
106
|
+
- db/migrate/20260715072550_create_instagram_connect_messages.rb
|
|
107
|
+
- db/migrate/20260715072551_create_instagram_connect_inbound_messages.rb
|
|
108
|
+
- db/migrate/20260715072552_create_instagram_connect_comments.rb
|
|
102
109
|
- docs/app_review_guide.md
|
|
103
110
|
- docs/auth_paths.md
|
|
104
111
|
- docs/roadmap.md
|
|
105
112
|
- lib/generators/instagram_connect/controllers_generator.rb
|
|
106
113
|
- lib/generators/instagram_connect/install/install_generator.rb
|
|
107
|
-
- lib/generators/instagram_connect/install/templates/create_instagram_connect_accounts.rb.tt
|
|
108
|
-
- lib/generators/instagram_connect/install/templates/create_instagram_connect_comments.rb.tt
|
|
109
|
-
- lib/generators/instagram_connect/install/templates/create_instagram_connect_conversations.rb.tt
|
|
110
|
-
- lib/generators/instagram_connect/install/templates/create_instagram_connect_inbound_messages.rb.tt
|
|
111
|
-
- lib/generators/instagram_connect/install/templates/create_instagram_connect_messages.rb.tt
|
|
112
114
|
- lib/generators/instagram_connect/install/templates/instagram_connect.rb.tt
|
|
113
115
|
- lib/generators/instagram_connect/views_generator.rb
|
|
114
116
|
- lib/instagram_connect.rb
|