PluginProbe
codoc / 0.9.61
codoc v0.9.61
0.9.61 0.9.8.8 0.9.8.9 0.9.9 0.9.9.1 trunk 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.8.1 0.8.2 0.8.3 0.8.4 0.8.6 0.8.7 0.8.8 0.8.9 0.9 0.9.1 0.9.10 All 115 releases
← All changes | src/block/block.js +539 -165 0.1 → 0.9.61 View file →
@@ -7,11 +7,14 @@
7 7
8 8 // Import CSS.
9 9 import './style.scss';
10 10 import './editor.scss';
11 -
11 +import icon from './icon';
12 +//import { ENTER } from '@wordpress/keycodes';
13 +const { ENTER } = wp.keycodes;
12 14 const { __ } = wp.i18n; // Import __() from wp.i18n
13 15 const { Component } = wp.element;
16 +
14 17 const { registerBlockType } = wp.blocks; // Import registerBlockType() from wp.blocks
15 18 const {
16 19 InspectorControls,
17 20 RichText
@@ -19,26 +22,174 @@
19 22
20 23 const {
21 24 SelectControl,
22 25 PanelBody,
23 -} = wp.components
26 + ToggleControl,
27 + CheckboxControl,
28 + RangeControl,
29 + TextControl,
30 + Notice,
31 +} = wp.components;
24 32
25 -const codocURL = 'https://codoc.jp';
26 -const codocEntriesClass = 'codoc-entries';
33 +const { withSelect } = wp.data;
27 34
28 -import Cookies from 'universal-cookie';
29 -const cookies = new Cookies();
35 +const {
36 + getDefaultBlockName,
37 + createBlock,
38 +} = wp.blocks;
30 39
40 +const { withState } = wp.compose;
41 +
42 +const CODOC_URL = OPTIONS.codoc_url;
43 +const CODOC_USER_CODE = OPTIONS.codoc_usercode;
44 +const CODOC_PLUGIN_VERSION = OPTIONS.codoc_plugin_version;
45 +const CODOC_ENTRIES_CLASS = 'codoc-entries';
46 +const CODOC_ACCOUNT_IS_PRO = OPTIONS.codoc_account_is_pro;
47 +const CODOC_CURRENCY_CODE = OPTIONS.codoc_currency_code;
48 +const CODOC_CURRENCY_DECIMAL_PLACES = OPTIONS.codoc_currency_decimal_places;
49 +
50 +//import Cookies from 'universal-cookie';
51 +//const cookies = new Cookies();
52 +
53 +const CODOC_MAX_LENGTHS = OPTIONS.codoc_max_lengths || {};
54 +
55 +// エディタで前回使った設定を localStorage に保存し、新規ブロックの初期値に使う
56 +// (優先順位: 設定画面の Block Default Values -> 前回値 -> 固定値)
57 +const CODOC_LAST_USED_KEY = 'codoc_block_last_used';
58 +const CODOC_LAST_USED_ATTRS = ['showPrice','price','limited','limitedCount','affiliateMode','affiliateRate','showSupport','showPaywalledSupport','showPayWithoutAccountButton','statusLimited','subscriptions'];
59 +function loadLastUsedAttributes() {
60 + try {
61 + const parsed = JSON.parse(window.localStorage.getItem(CODOC_LAST_USED_KEY) || 'null');
62 + return (parsed && typeof parsed === 'object') ? parsed : {};
63 + } catch (e) {
64 + return {};
65 + }
66 +}
67 +function saveLastUsedAttributes(attributes) {
68 + try {
69 + const saved = {};
70 + CODOC_LAST_USED_ATTRS.forEach((key) => {
71 + if (attributes[key] !== undefined && attributes[key] !== null) {
72 + saved[key] = attributes[key];
73 + }
74 + });
75 + window.localStorage.setItem(CODOC_LAST_USED_KEY, JSON.stringify(saved));
76 + } catch (e) {
77 + // localStorage が使えない環境では何もしない
78 + }
79 +}
80 +
81 +// codoc ブロックの直前/直後で本文を分割
82 +function splitCodocContent(content) {
83 + const blockRegex = /<!--\s*wp:codoc\/codoc-block[\s\S]*?<!--\s*\/wp:codoc\/codoc-block\s*-->/;
84 + const splited = (content || '').split(blockRegex);
85 + return {
86 + bodyFree: splited[0] || '',
87 + bodyPaywalled: splited.slice(1).join('') || '',
88 + };
89 +}
90 +
91 +function buildLengthErrors(title, bodyFree, bodyPaywalled) {
92 + const errors = [];
93 + const total = bodyFree.length + bodyPaywalled.length;
94 + if (CODOC_MAX_LENGTHS.title && title.length > CODOC_MAX_LENGTHS.title) {
95 + errors.push(__('Title exceeds the maximum length of', 'codoc') + ' ' + CODOC_MAX_LENGTHS.title + ' (' + title.length + ')');
96 + }
97 + if (CODOC_MAX_LENGTHS.body_free && bodyFree.length > CODOC_MAX_LENGTHS.body_free) {
98 + errors.push(__('Free area exceeds the maximum length of', 'codoc') + ' ' + CODOC_MAX_LENGTHS.body_free + ' (' + bodyFree.length + ')');
99 + }
100 + if (CODOC_MAX_LENGTHS.body_paywalled && bodyPaywalled.length > CODOC_MAX_LENGTHS.body_paywalled) {
101 + errors.push(__('Paid area exceeds the maximum length of', 'codoc') + ' ' + CODOC_MAX_LENGTHS.body_paywalled + ' (' + bodyPaywalled.length + ')');
102 + }
103 + if (CODOC_MAX_LENGTHS.body && total > CODOC_MAX_LENGTHS.body) {
104 + errors.push(__('Total body exceeds the maximum length of', 'codoc') + ' ' + CODOC_MAX_LENGTHS.body + ' (' + total + ')');
105 + }
106 + return errors;
107 +}
108 +
109 +// codoc ブロック選択時の Inspector サイドバーに警告を表示する
110 +const CodocLengthNotices = withSelect((select) => {
111 + const editor = select('core/editor');
112 + return {
113 + postTitle: editor ? editor.getEditedPostAttribute('title') : '',
114 + postContent: editor ? editor.getEditedPostAttribute('content') : '',
115 + };
116 +})(({ postTitle, postContent }) => {
117 + const { bodyFree, bodyPaywalled } = splitCodocContent(postContent);
118 + const errors = buildLengthErrors(postTitle || '', bodyFree, bodyPaywalled);
119 + if (errors.length === 0) {
120 + return null;
121 + }
122 + return (
123 + <Notice status="error" isDismissible={ false }>
124 + <strong>{ __('codoc length limit exceeded:', 'codoc') }</strong>
125 + <ul style={{ marginTop: '0.5em', marginBottom: 0 }}>
126 + { errors.map((e, i) => <li key={i}>{ e }</li>) }
127 + </ul>
128 + </Notice>
129 + );
130 +});
131 +
132 +// codoc ブロックを選択していなくても、本文中に存在すればエディタ上部に通知を表示する
133 +(function() {
134 + if (!wp.data || !wp.data.subscribe) {
135 + return;
136 + }
137 + const NOTICE_ID = 'codoc-length-limit';
138 + let lastState = '';
139 + wp.data.subscribe(function() {
140 + const editor = wp.data.select('core/editor');
141 + const blockEditor = wp.data.select('core/block-editor') || wp.data.select('core/editor');
142 + const noticesDispatch = wp.data.dispatch('core/notices');
143 + if (!editor || !blockEditor || !noticesDispatch) {
144 + return;
145 + }
146 + const blocks = blockEditor.getBlocks ? blockEditor.getBlocks() : [];
147 + const hasCodocBlock = blocks.some(function(b) { return b && b.name === 'codoc/codoc-block'; });
148 + if (!hasCodocBlock) {
149 + if (lastState !== '') {
150 + noticesDispatch.removeNotice(NOTICE_ID);
151 + lastState = '';
152 + }
153 + return;
154 + }
155 + const title = editor.getEditedPostAttribute('title') || '';
156 + const content = editor.getEditedPostAttribute('content') || '';
157 + const { bodyFree, bodyPaywalled } = splitCodocContent(content);
158 + const errors = buildLengthErrors(title, bodyFree, bodyPaywalled);
159 + const state = errors.join('||');
160 + if (state === lastState) {
161 + return;
162 + }
163 + lastState = state;
164 + noticesDispatch.removeNotice(NOTICE_ID);
165 + if (errors.length > 0) {
166 + noticesDispatch.createNotice(
167 + 'error',
168 + __('codoc length limit exceeded:', 'codoc') + ' ' + errors.join(' / '),
169 + { id: NOTICE_ID, isDismissible: false }
170 + );
171 + }
172 + });
173 +})();
174 +
31 175 class CodocControls extends Component {
32 176 constructor( props ) {
33 177 super( ...arguments );
178 + this.subscriptionsFetched = [];
179 + this.subscriptionSearchTerm = '';
180 + this.state = {
181 + searchTerm: '',
182 + filteredSubscriptions: []
183 + };
184 + this.fetchSubscriptions();
185 + this.handleSearchChange = this.handleSearchChange.bind(this);
34 186 }
35 187
36 - fetchEntries(userCode,entryCode) {
188 + fetchSubscriptions(searchTitle = '') {
37 189 const {
38 190 setAttributes,
39 191 attributes: {
40 - entries,
41 192 fetching,
42 193 }
43 194 } = this.props;
44 195
@@ -46,11 +197,13 @@
46 197 return
47 198 }
48 199
49 200 setAttributes( { fetching: true } )
50 - let url = codocURL + '/api/v1/paywall/' + userCode + '/entries'
51 - if (entryCode) {
52 - url = url + '/' + entryCode
201 + let url = CODOC_URL + '/api/v1/cms/' + CODOC_USER_CODE + '/subscriptions?without_token=1';
202 +
203 + // Add title parameter if search term exists
204 + if (searchTitle) {
205 + url += '&title=' + encodeURIComponent(searchTitle);
53 206 }
54 207
55 208 fetch(url)
56 209 .then( res => res.json() )
@@ -55,129 +208,289 @@
55 208 fetch(url)
56 209 .then( res => res.json() )
57 210 .then( res => {
58 211 let list = [];
59 - let eyecatchMap = [];
60 - if (res.status && res.entries) {
61 - for ( var i = 0; i < res.entries.length; i++ ) {
212 + if (res.status && res.subscriptions) {
213 + for ( var i = 0; i < res.subscriptions.length; i++ ) {
62 214 let info = {
63 - value: res.entries[i].code,
64 - label: res.entries[i].title,
215 + value: res.subscriptions[i].code,
216 + label: res.subscriptions[i].title,
217 + term: res.subscriptions[i].term,
218 + price: res.subscriptions[i].price,
219 + currency: res.subscriptions[i].currency
65 220 }
66 221 list[i] = info
67 - eyecatchMap[res.entries[i].code] = res.entries[i].image_url
68 222 }
69 223 }
70 - if (list.length) {
71 - setAttributes( { entries: list })
72 - setAttributes( { entryCode: list[0].value } );
73 - setAttributes( { entryLabel: list[0].label } );
74 - setAttributes( { entryImgSrc: eyecatchMap[list[0].value] } );
75 - setAttributes( { eyecatchMap: eyecatchMap });
224 + if (list.length || searchTitle) {
225 + this.subscriptionsFetched = list;
226 + this.setState({ filteredSubscriptions: list });
76 227 }
77 228 setAttributes( { fetching: false } )
78 229 })
79 230 }
80 231
81 - getEntryLabel(entryCode) {
82 - const {
83 - attributes: {
84 - entries,
85 - }
86 - } = this.props;
232 + handleSearchChange(value) {
233 + this.setState({ searchTerm: value });
87 234
88 - for ( var i = 0; i < entries.length; i++ ) {
89 - if (entries[i].value === entryCode ) {
90 - return entries[i].label
91 - }
92 - }
235 + // Debounce the API call
236 + clearTimeout(this.searchTimeout);
237 + this.searchTimeout = setTimeout(() => {
238 + this.fetchSubscriptions(value);
239 + }, 500);
93 240 }
94 241
242 + getShowPriceHelp( checked ) {
243 + return checked ?
244 + __( 'Enable','codoc' ) :
245 + __( 'Disable','codoc' );
246 + }
247 + getShowSupportHelp( checked ) {
248 + return checked ?
249 + __( 'Accept' ,'codoc') :
250 + __( 'Do not accept' ,'codoc');
251 + }
252 + getShowPaywalledSupportHelp( checked ) {
253 + return checked ?
254 + __( 'The paid part will be hidden upon access, but will be displayed after tapping "Pay to Read". Viewers can freely specify the amount to purchase based on the content. Please note that there may be cases where it is not purchased.' ,'codoc') :
255 + __( ' ' );
256 + }
257 + getStatusLimitedHelp( checked ) {
258 + return checked ?
259 + __( 'Unlisted' ,'codoc') :
260 + __( 'Published' ,'codoc');
261 + }
262 + getShowPayWithoutAccountButtonHelp( checked ) {
263 + return checked ?
264 + __( 'Viewers can purchase without a codoc account.' ,'codoc') :
265 + __( 'Viewers must log in to codoc to purchase.' ,'codoc');
266 + }
267 +
95 268 render() {
96 269 const {
97 - setAttributes,
270 + setAttributes: setBlockAttributes,
98 271 attributes: {
99 - userCode,
100 - entryCode,
101 - entryLabel,
102 - entries,
103 - eyecatchMap,
272 + showPrice,
273 + price,
274 + limited,
275 + limitedCount,
276 +
277 + affiliateMode,
278 + affiliateRate,
279 +
280 + showSupport,
281 + showPaywalledSupport,
282 + showPayWithoutAccountButton,
283 + statusLimited,
284 + subscriptions,
104 285 }
105 286 } = this.props;
106 - var entriesOptions = entries
287 + // 設定変更のたびに前回値として保存
288 + const setAttributes = (attrs) => {
289 + setBlockAttributes(attrs);
290 + saveLastUsedAttributes(Object.assign({}, this.props.attributes, attrs));
291 + };
107 292
108 - // edit
109 - if (userCode && entryCode && entryLabel && !entries) {
110 - entriesOptions = [ { value: entryCode, label:entryLabel } ]
111 - setAttributes({ entries: entriesOptions })
112 - }
113 - // if it's userCode in cookies
114 - if (!userCode) {
115 - let userCodeInCookie = cookies.get('codocUserCode');
116 - if (userCodeInCookie) {
117 - setAttributes( { userCode: userCodeInCookie } );
118 - this.fetchEntries(userCodeInCookie)
293 + const toggleShowPrice = () => setAttributes( { showPrice: ! showPrice } );
294 + const toggleLimited = () => setAttributes( { limited: ! limited } );
295 + const toggleShowPaywalledSupport = () => setAttributes( { showPaywalledSupport: ! showPaywalledSupport } );
296 + const toggleStatusLimited = () => setAttributes( { statusLimited: ! statusLimited } );
297 + const toggleAffiliateMode = () => setAttributes( { affiliateMode: ! affiliateMode } );
298 + const toggleShowSupport = () => setAttributes( { showSupport: ! showSupport } );
299 + const toggleShowPayWithoutAccountButton = () => setAttributes( { showPayWithoutAccountButton: ! showPayWithoutAccountButton } );
300 +
301 + const SubscriptionCheckBoxes = withState({
302 + checked_obj: Object.assign({}, subscriptions)
303 + })( ({ checked_obj, setState }) => (
304 + <div>
305 + {this.state.filteredSubscriptions.length === 0 ? (
306 + <p style={{ fontStyle: 'italic', color: '#666' }}>
307 + { this.state.searchTerm
308 + ? __('No plans found matching your search.','codoc')
309 + : __('No reader plans available.','codoc')
310 + }
311 + </p>
312 + ) : (
313 + <ul>
314 + {
315 + this.state.filteredSubscriptions.map((v) => {
316 + const isChecked = !!checked_obj[v.value];
317 + const checkedCount = Object.keys(checked_obj).length;
318 +
319 + return (
320 + <li key={v.value}>
321 + <CheckboxControl
322 + className="check_items"
323 + label={v.label}
324 + checked={isChecked}
325 + onChange={(check) => {
326 + // 追加する場合、5個以上は無効
327 + if (check && checkedCount >= 5) {
328 + return; // これ以上チェックできない
329 + }
330 +
331 + const newChecked = { ...checked_obj };
332 +
333 + if (check) {
334 + newChecked[v.value] = true;
335 + } else {
336 + delete newChecked[v.value];
337 + }
338 +
339 + setAttributes({ subscriptions: newChecked });
340 + setState({ checked_obj: newChecked });
341 + }}
342 + />
343 + </li>
344 + );
345 + })
119 346 }
120 - }
121 - // first time
122 - if (!entriesOptions) {
123 - let entriesDefault = [ { value: '-', label: __( 'ユーザーコードを入力してください' )} ]
124 - setAttributes( { entries: entriesDefault })
125 - setAttributes( { entryLabel: 'ブロック設定を開いてユーザーコードを入力してください' } )
126 - }
347 + </ul>
348 + )}
349 + </div>
350 + ) )
351 + const affiliateRateOptions = [
352 + { value: '0.0500', label:'5%' },
353 + { value: '0.1000', label:'10%' },
354 + { value: '0.1500', label:'15%' },
355 + { value: '0.2000', label:'20%' },
356 + { value: '0.2500', label:'25%' },
357 + { value: '0.3000', label:'30%' },
358 + { value: '0.3500', label:'35%' },
359 + { value: '0.4000', label:'40%' },
360 + { value: '0.4500', label:'45%' },
361 + { value: '0.5000', label:'50%' },
362 + ];
127 363
128 364 return(
129 365 <InspectorControls key="CodocControls">
366 +
367 + <CodocLengthNotices />
368 +
130 369 <PanelBody>
131 - <label>ユーザーコード</label>
132 - <label>
133 - <a
134 - href={ codocURL + '/me/account?from_wp=1' }
135 - target="_blank"
136 - >確認↗️</a>
137 - </label>
138 - <RichText
139 - tagName="div"
140 - placeholder={ __( 'ここにコピーして貼付け' ) }
141 - value={ userCode }
142 - key="usercodeform"
143 - unstableOnSplit={ () => false }
370 +
371 + <ToggleControl
372 + label={ __( 'individual sale','codoc' ) }
373 + checked={ !! showPrice }
374 + help={ this.getShowPriceHelp }
375 + ref="showPrice"
376 + onChange={ toggleShowPrice }
377 + />
378 +
379 + <div style={{ display: showPrice ? 'initial' : 'none' }}>
380 + <RangeControl
381 + label={ __('price','codoc') + ' ' + (CODOC_CURRENCY_DECIMAL_PLACES > 0 ? (price / 100).toFixed(CODOC_CURRENCY_DECIMAL_PLACES) : price) + ' ' + __(CODOC_CURRENCY_CODE) + __(showPaywalledSupport ? '【' + __('Displayed as the your suggested price','codoc') + '】' : '' ) }
382 + value={ price }
383 + initialPosition={ price }
144 384 onChange={ ( value ) => {
145 - setAttributes( { userCode: value } );
146 - var dt = new Date();
147 - dt.setMonth(dt.getMonth() + 12);
148 - cookies.set('codocUserCode', value, { path: '/', expires: dt });
149 - this.fetchEntries(value)
150 - }}/><br />
151 - <label>記事コード</label>
152 - <RichText
153 - tagName="div"
154 - placeholder={ __( '' ) }
155 - key="usercodeform"
156 - value={ entryCode }
157 - unstableOnSplit={ () => false }
385 + // do validate this
386 + setAttributes({ price: value });
387 + }}
388 + min="100"
389 + max={ CODOC_ACCOUNT_IS_PRO == 1 ? 100000 : 50000 }
390 + /><br />
391 +
392 + <ToggleControl
393 + label={ __( 'Pay-what-you-want','codoc' ) }
394 + checked={ !! showPaywalledSupport }
395 + help={ this.getShowPaywalledSupportHelp }
396 + onChange={ toggleShowPaywalledSupport }
397 + />
398 +
399 + <div style={{ display: showPaywalledSupport ? 'none' : 'initial' }}>
400 + <ToggleControl
401 + label={ __( 'Limited quantity sale','codoc' ) }
402 + checked={ !! limited }
403 + onChange={ toggleLimited }
404 + />
405 +
406 + <div style={{ display: limited ? 'initial' : 'none' }}>
407 + <RangeControl
408 + label={ __( 'Limited quantity','codoc' ) }
409 + value={ limitedCount }
158 410 onChange={ ( value ) => {
159 - this.fetchEntries(cookies.get('codocUserCode'),value)
160 - }}/><br />
411 + // do validate this
412 + setAttributes({ limitedCount: value });
413 + }}
414 + initialPosition={ limitedCount }
415 + min="0"
416 + max="100"
417 + /><br />
418 + </div>
419 +
420 + <ToggleControl
421 + label={ __( 'Affiliate','codoc' ) }
422 + checked={ !! affiliateMode }
423 + onChange={ toggleAffiliateMode }
424 + />
425 +
426 + <div style={{ display: affiliateMode ? 'initial' : 'none' }}>
161 427 <SelectControl
162 - label={ __( '記事一覧' ) }
163 - description={ __( '貼り付ける記事を選んでください' ) }
164 - options={ entriesOptions }
165 - value={ entryCode }
428 + label={ __( 'Rate' , 'codoc') }
429 + description={ __( 'You can offer affiliate marketing at a specified rate for purchasers.', 'codoc') }
430 + options={ affiliateRateOptions }
431 + value={ affiliateRate }
166 432 onChange={ ( value ) => {
167 - setAttributes( { entryCode: value } );
168 - setAttributes( { entryLabel: this.getEntryLabel(value) } );
169 - setAttributes( { entryImgSrc: eyecatchMap[value] } );
170 - }}/>
171 - <button
172 - onClick={ ( value ) => {
173 - this.fetchEntries(userCode)
174 - }}>記事一覧を再取得</button>
433 + setAttributes( { affiliateRate: value } );
434 + }}/><br />
435 + </div>
436 +
437 + </div>
438 + </div>
439 +
440 + <div style={{ display: showPaywalledSupport ? 'none' : 'initial' }}>
441 + <ToggleControl
442 + label={ __( 'Tipping', 'codoc') }
443 + checked={ !! showSupport }
444 + value="1"
445 + onChange={ toggleShowSupport }
446 + help={ this.getShowSupportHelp }
447 + />
448 + </div>
449 +
450 + <div style={{ display: 'initial' }}>
451 + <ToggleControl
452 + label={ __( 'Guest purchase', 'codoc') }
453 + checked={ !! showPayWithoutAccountButton }
454 + value="1"
455 + onChange={ toggleShowPayWithoutAccountButton }
456 + help={ this.getShowPayWithoutAccountButtonHelp }
457 + />
458 + </div>
459 +
460 + <div style={{ display: 'initial' }}>
461 + <ToggleControl
462 + label={ __( 'Unlisted','codoc' ) }
463 + checked={ !! statusLimited }
464 + value="1"
465 + onChange={ toggleStatusLimited }
466 + help={ this.getStatusLimitedHelp }
467 + />
468 + </div>
469 +
470 + <div class="codoc-subscription-title">
471 + <label>
472 + { __('Reader Plans','codoc') }
473 + </label>
474 + <a
475 + href={ CODOC_URL + '/me/subscriptions'}
476 + target="_blank" class="codoc-subscription-add"
477 + >
478 + { __('Add','codoc') }
479 + </a>
480 + </div>
481 +
482 + <TextControl
483 + label={ __('Search Plans','codoc') }
484 + value={ this.state.searchTerm }
485 + onChange={ this.handleSearchChange }
486 + placeholder={ __('Enter title to search...','codoc') }
487 + />
488 +
489 + <SubscriptionCheckBoxes />
490 +
491 +
175 492 </PanelBody>
176 - <a
177 - href={ codocURL + '/me/entries/create?from_wp=1' }
178 - target="_blank"
179 - >記事を作成↗️</a>
180 493 </InspectorControls>
181 494 );
182 495 }
183 496 };
@@ -182,47 +495,76 @@
182 495 }
183 496 };
184 497
185 498 class EditBlockContent extends Component {
499 + constructor() {
500 + super( ...arguments );
501 + // デフォルト値
502 + const bd = OPTIONS.codoc_block_defaults || {};
503 + const lu = loadLastUsedAttributes();
504 + const d = (key, fallback) => bd.hasOwnProperty(key) ? bd[key] : (lu.hasOwnProperty(key) ? lu[key] : fallback);
505 + this.props.setAttributes({ version: CODOC_PLUGIN_VERSION });
506 + this.props.setAttributes({ showPrice: this.props.attributes.showPrice == null ? d('showPrice', true) : this.props.attributes.showPrice });
507 + this.props.setAttributes({ price: this.props.attributes.price == null ? d('price', 500) : this.props.attributes.price });
508 + this.props.setAttributes({ limited: this.props.attributes.limited == null ? d('limited', false) : this.props.attributes.limited });
509 + this.props.setAttributes({ limitedCount: this.props.attributes.limitedCount == null ? d('limitedCount', 10) : this.props.attributes.limitedCount });
510 + this.props.setAttributes({ affiliateMode: this.props.attributes.affiliateMode == null ? d('affiliateMode', false) : this.props.attributes.affiliateMode });
511 + this.props.setAttributes({ affiliateRate: this.props.attributes.affiliateRate == null ? d('affiliateRate', '0.0500') : this.props.attributes.affiliateRate });
512 + this.props.setAttributes({ showSupport: this.props.attributes.showSupport == null ? d('showSupport', false) : this.props.attributes.showSupport });
513 + this.props.setAttributes({ showPaywalledSupport: this.props.attributes.showPaywalledSupport == null ? d('showPaywalledSupport', false) : this.props.attributes.showPaywalledSupport });
514 + this.props.setAttributes({ statusLimited: this.props.attributes.statusLimited == null ? d('statusLimited', false) : this.props.attributes.statusLimited });
515 + this.props.setAttributes({ showPayWithoutAccountButton: this.props.attributes.showPayWithoutAccountButton == null ? d('showPayWithoutAccountButton', true) : this.props.attributes.showPayWithoutAccountButton });
516 + this.props.setAttributes({ subscriptions: this.props.attributes.subscriptions == null ? d('subscriptions', {}) : this.props.attributes.subscriptions });
517 +
518 + this.onChangeInput = this.onChangeInput.bind( this );
519 + this.onKeyDown = this.onKeyDown.bind( this );
520 + this.state = {
521 + defaultText: __( 'To continue reading, ...','codoc' ),
522 + };
523 + }
524 + onChangeInput( event ) {
525 + this.setState( {
526 + defaultText: '',
527 + } );
528 +
529 + const value = event.target.value.length === 0 ? undefined : event.target.value;
530 + this.props.setAttributes( { customText: value } );
531 + }
532 + onKeyDown( event ) {
533 + const { keyCode } = event;
534 + const { insertBlocksAfter } = this.props;
535 + if ( keyCode === ENTER ) {
536 + insertBlocksAfter( [ createBlock( getDefaultBlockName() ) ] );
537 + }
538 + }
539 +
186 540 render() {
187 541 const {
188 542 attributes: {
189 - id,
190 - text,
191 - userCode,
192 - entryCode,
193 - entryLabel,
194 - entryImgSrc,
543 + customText,
195 544 },
196 545 setAttributes
197 546 } = this.props;
198 547
548 + const { defaultText } = this.state;
549 + //const value = customText !== undefined ? customText : defaultText;
550 + const value = customText ? customText : defaultText;
551 + //const value = defaultText;
552 + const inputLength = value.length + 10;
553 +
199 554 return[
200 555 <CodocControls { ...{setAttributes, ...this.props } } />,
201 - <div className="codoc-block">
202 - <div>
203 - <div>
204 - <img
205 - width="50%"
206 - height="50%"
207 - src={ entryImgSrc } />
208 - </div>
209 - <span>{ entryLabel }</span>
210 - <span>
211 - <a
212 - href={ codocURL + '/me/entries/' + entryCode + '/edit' }
213 - target="_blank">【編集↗️】</a>
214 - </span>
215 - </div>
216 - <RichText
217 - tagName="div"
218 - placeholder={ __( 'この続きはcodocで購読' ) }
219 - keepPlaceholderOnFocus
220 - className="codoc-block-link"
221 - value={ text }
222 - onChange={ ( value ) => setAttributes( { text: value } )}
223 - />
224 - </div>
556 +
557 + <div className="wp-block-more">
558 + <input
559 + type="text"
560 + value={ value }
561 + size={ inputLength }
562 + onChange={ this.onChangeInput }
563 + onKeyDown={ this.onKeyDown }
564 + />
565 + </div>
566 +
225 567 ];
226 568 }
227 569 };
228 570
@@ -227,65 +569,97 @@
227 569 };
228 570
229 571
230 572 registerBlockType( 'codoc/codoc-block', {
231 - title: __( 'codoc Block' ),
232 - icon: 'book',
233 - category: 'common',
573 + title: __( 'codoc' ,'codoc'),
574 + description: __( 'The area from this block down is a paid area that only authenticated codoc users can view.','codoc' ),
575 + icon,
576 + category: 'layout',
577 + supports: {
578 + customClassName: false,
579 + className: false,
580 + html: false,
581 + multiple: false,
582 + },
234 583 keywords: [
235 - __( 'codoc Block' ),
236 - __( 'plugin for codoc' ),
237 - __( 'codoc-block' ),
584 + __( 'codoc Block' ,'codoc'),
585 + __( 'plugin for codoc' ,'codoc'),
586 + __( 'codoc-block' ,'codoc'),
238 587 ],
239 588 attributes: {
240 - entryCode: {
589 + showPrice: {
590 + type: 'boolean',
591 + default: null,
592 + },
593 + price: {
594 + type: 'number',
595 + default: null,
596 + },
597 + limited: {
598 + type: 'boolean',
599 + default: null,
600 + },
601 + limitedCount: {
602 + type: 'number',
603 + default: null,
604 + },
605 + affiliateMode: {
606 + type: 'boolean',
607 + default: null,
608 + },
609 + affiliateRate: {
241 610 type: 'string',
242 - default: '',
611 + default: null,
243 612 },
244 - entryLabel: {
245 - type: 'string',
246 - default: '',
613 + showSupport: {
614 + type: 'boolean',
615 + default: null,
247 616 },
248 - userCode: {
249 - type: 'string',
250 - default: '',
617 + showPaywalledSupport: {
618 + type: 'boolean',
619 + default: null,
251 620 },
252 - entryImgSrc: {
621 + statusLimited: {
622 + type: 'boolean',
623 + default: null,
624 + },
625 + showPayWithoutAccountButton: {
626 + type: 'boolean',
627 + default: null,
628 + },
629 + subscriptions: {
630 + type: 'object',
631 + default: null,
632 + },
633 + customText: {
253 634 type: 'string',
254 635 default: '',
255 636 },
256 - text: {
637 + version: {
257 638 type: 'string',
258 - default: '',
639 + default: null,
259 640 },
260 641 },
261 -
642 +
262 643 edit: EditBlockContent,
263 -
644 +
264 645 save: function( props ) {
265 646 const {
266 647 setAttributes,
267 648 attributes: {
268 - // if you need to save datas, check attributes of registerBlockType's attributes
269 - text,
270 - userCode,
271 - entryCode,
272 - entryLabel,
273 - entryImgSrc,
649 + customText,
274 650 }
275 651 } = props;
276 652 return ( // return if link behavior normal
277 - <div>
278 - <span
279 - id={ 'codoc-entry-' + entryCode }
280 - className={ codocEntriesClass }
653 + <div
654 + data-id='codoc-tag'
655 + className={ CODOC_ENTRIES_CLASS }
281 656 >
282 - { text && !! text.length && (
657 + { customText && !! customText.length && (
283 658 <RichText.Content
284 - value={ text }
659 + value={ customText }
285 660 />
286 661 )}
287 - </span>
288 662 </div>
289 663 )
290 664 },
291 665 } );